// Spooky Hash
// A 128-bit noncryptographic hash, for checksums and table lookup
// By Bob Jenkins.  Public domain.
//   Oct 31 2010: published framework, disclaimer ShortHash isn't right
//   Nov 7 2010: disabled ShortHash
//   Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again
//   April 10 2012: buffer overflow on platforms without unaligned reads

/**
 * @file   spooky.cc
 * @author Bob Jenkins
 * @date   Tue Dec 17 13:47:55 2013
 * 
 * @brief  Spooky Hash from Bob Jenkins. This is public domain.
 */

#include <memory.h>
#include "spooky.h"
#line 22 "spooky.cc"

#define ALLOW_UNALIGNED_READS 1

//
// short hash ... it could be used on any message,
// but it's used by Spooky just for short messages.
//
void SpookyHash::Short(
  const void *message,
  size_t length,
  uint64 *hash1,
  uint64 *hash2)
{
  uint64 buf[2*sc_numVars];
  union
  {
    const uint8 *p8;
    uint32 *p32;
    uint64 *p64;
    size_t i;
  } u;

  u.p8 = (const uint8 *)message;

  if (!ALLOW_UNALIGNED_READS && (u.i & 0x7))
  {
    memcpy(buf, message, length);
    u.p64 = buf;
  }

  size_t remainder = length%32;
  uint64 a=*hash1;
  uint64 b=*hash2;
  uint64 c=sc_const;
  uint64 d=sc_const;

  if (length > 15)
  {
    const uint64 *end = u.p64 + (length/32)*4;

    // handle all complete sets of 32 bytes
    for (; u.p64 < end; u.p64 += 4)
    {
      c += u.p64[0];
      d += u.p64[1];
      ShortMix(a,b,c,d);
      a += u.p64[2];
      b += u.p64[3];
    }

    //Handle the case of 16+ remaining bytes.
    if (remainder >= 16)
    {
      c += u.p64[0];
      d += u.p64[1];
      ShortMix(a,b,c,d);
      u.p64 += 2;
      remainder -= 16;
    }
  }

  // Handle the last 0..15 bytes, and its length
  d = ((uint64)length) << 56;

  switch (remainder)
  {
    case 15:
      d += ((uint64)u.p8[14]) << 48;

    case 14:
      d += ((uint64)u.p8[13]) << 40;

    case 13:
      d += ((uint64)u.p8[12]) << 32;

    case 12:
      d += u.p32[2];
      c += u.p64[0];
      break;

    case 11:
      d += ((uint64)u.p8[10]) << 16;

    case 10:
      d += ((uint64)u.p8[9]) << 8;

    case 9:
      d += (uint64)u.p8[8];

    case 8:
      c += u.p64[0];
      break;

    case 7:
      c += ((uint64)u.p8[6]) << 48;

    case 6:
      c += ((uint64)u.p8[5]) << 40;

    case 5:
      c += ((uint64)u.p8[4]) << 32;

    case 4:
      c += u.p32[0];
      break;

    case 3:
      c += ((uint64)u.p8[2]) << 16;

    case 2:
      c += ((uint64)u.p8[1]) << 8;

    case 1:
      c += (uint64)u.p8[0];
      break;

    case 0:
      c += sc_const;
      d += sc_const;
  }

  ShortEnd(a,b,c,d);
  *hash1 = a;
  *hash2 = b;
}




// do the whole hash in one call
void SpookyHash::Hash128(
  const void *message,
  size_t length,
  uint64 *hash1,
  uint64 *hash2)
{
  if (length < sc_bufSize)
  {
    Short(message, length, hash1, hash2);
    return;
  }

  uint64 h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;
  uint64 buf[sc_numVars];
  uint64 *end;
  union
  {
    const uint8 *p8;
    uint64 *p64;
    size_t i;
  } u;
  size_t remainder;

  h0=h3=h6=h9  = *hash1;
  h1=h4=h7=h10 = *hash2;
  h2=h5=h8=h11 = sc_const;

  u.p8 = (const uint8 *)message;
  end = u.p64 + (length/sc_blockSize)*sc_numVars;

  // handle all whole sc_blockSize blocks of bytes
  if (ALLOW_UNALIGNED_READS || ((u.i & 0x7) == 0))
  {
    while (u.p64 < end)
    {
      Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
      u.p64 += sc_numVars;
    }
  }
  else
  {
    while (u.p64 < end)
    {
      memcpy(buf, u.p64, sc_blockSize);
      Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
      u.p64 += sc_numVars;
    }
  }

  // handle the last partial block of sc_blockSize bytes
  remainder = (length - ((const uint8 *)end-(const uint8 *)message));
  memcpy(buf, end, remainder);
  memset(((uint8 *)buf)+remainder, 0, sc_blockSize-remainder);
  ((uint8 *)buf)[sc_blockSize-1] = remainder;
  Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);

  // do some final mixing
  End(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
  *hash1 = h0;
  *hash2 = h1;
}



// init spooky state
void SpookyHash::Init(uint64 seed1, uint64 seed2)
{
  m_length = 0;
  m_remainder = 0;
  m_state[0] = seed1;
  m_state[1] = seed2;
}


// add a message fragment to the state
void SpookyHash::Update(const void *message, size_t length)
{
  uint64 h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;
  size_t newLength = length + m_remainder;
  uint8  remainder;
  union
  {
    const uint8 *p8;
    uint64 *p64;
    size_t i;
  } u;
  const uint64 *end;

  // Is this message fragment too short?  If it is, stuff it away.
  if (newLength < sc_bufSize)
  {
    memcpy(&((uint8 *)m_data)[m_remainder], message, length);
    m_length = length + m_length;
    m_remainder = (uint8)newLength;
    return;
  }

  // init the variables
  if (m_length < sc_bufSize)
  {
    h0=h3=h6=h9  = m_state[0];
    h1=h4=h7=h10 = m_state[1];
    h2=h5=h8=h11 = sc_const;
  }
  else
  {
    h0 = m_state[0];
    h1 = m_state[1];
    h2 = m_state[2];
    h3 = m_state[3];
    h4 = m_state[4];
    h5 = m_state[5];
    h6 = m_state[6];
    h7 = m_state[7];
    h8 = m_state[8];
    h9 = m_state[9];
    h10 = m_state[10];
    h11 = m_state[11];
  }

  m_length = length + m_length;

  // if we've got anything stuffed away, use it now
  if (m_remainder)
  {
    uint8 prefix = sc_bufSize-m_remainder;
    memcpy(&(((uint8 *)m_data)[m_remainder]), message, prefix);
    u.p64 = m_data;
    Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
    Mix(&u.p64[sc_numVars], h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
    u.p8 = ((const uint8 *)message) + prefix;
    length -= prefix;
  }
  else
  {
    u.p8 = (const uint8 *)message;
  }

  // handle all whole blocks of sc_blockSize bytes
  end = u.p64 + (length/sc_blockSize)*sc_numVars;
  remainder = (uint8)(length-((const uint8 *)end-u.p8));

  if (ALLOW_UNALIGNED_READS || (u.i & 0x7) == 0)
  {
    while (u.p64 < end)
    {
      Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
      u.p64 += sc_numVars;
    }
  }
  else
  {
    while (u.p64 < end)
    {
      memcpy(m_data, u.p8, sc_blockSize);
      Mix(m_data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
      u.p64 += sc_numVars;
    }
  }

  // stuff away the last few bytes
  m_remainder = remainder;
  memcpy(m_data, end, remainder);

  // stuff away the variables
  m_state[0] = h0;
  m_state[1] = h1;
  m_state[2] = h2;
  m_state[3] = h3;
  m_state[4] = h4;
  m_state[5] = h5;
  m_state[6] = h6;
  m_state[7] = h7;
  m_state[8] = h8;
  m_state[9] = h9;
  m_state[10] = h10;
  m_state[11] = h11;
}


// report the hash for the concatenation of all message fragments so far
void SpookyHash::Final(uint64 *hash1, uint64 *hash2)
{
  // init the variables
  if (m_length < sc_bufSize)
  {
    Short(m_data, m_length, hash1, hash2);
    return;
  }

  const uint64 *data = (const uint64 *)m_data;
  uint8 remainder = m_remainder;

  uint64 h0 = m_state[0];
  uint64 h1 = m_state[1];
  uint64 h2 = m_state[2];
  uint64 h3 = m_state[3];
  uint64 h4 = m_state[4];
  uint64 h5 = m_state[5];
  uint64 h6 = m_state[6];
  uint64 h7 = m_state[7];
  uint64 h8 = m_state[8];
  uint64 h9 = m_state[9];
  uint64 h10 = m_state[10];
  uint64 h11 = m_state[11];

  if (remainder >= sc_blockSize)
  {
    // m_data can contain two blocks; handle any whole first block
    Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
    data += sc_numVars;
    remainder -= sc_blockSize;
  }

  // mix in the last partial block, and the length mod sc_blockSize
  memset(&((uint8 *)data)[remainder], 0, (sc_blockSize-remainder));

  ((uint8 *)data)[sc_blockSize-1] = remainder;
  Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);

  // do some final mixing
  End(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);

  *hash1 = h0;
  *hash2 = h1;
}

